using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using FoundationLib.Ifc.Domain.Rules;


namespace FoundationLib.Ifc.Domain.Rules 
{
	/// <summary>
	/// Abstract base class for domain rules
	/// </summary>
	public abstract class RuleBase : IRule 
	{
		/// <summary>
		/// A list of members (attributes or properties) on the domain object which are
		/// affected by this domain rule
		/// </summary>
		private List<string> affectedMembers;
		/// <summary>
		/// The domain rule identifier
		/// </summary>
		private string id;

		/// <summary>
		/// Creates a new domain rule
		/// </summary>
		/// <param name="id">The domain rule identifier</param>
		protected RuleBase(string id) : this()
		{
			if (String.IsNullOrWhiteSpace(id))
				throw new ArgumentNullException("id");
			this.id = id;
		}

		/// <summary>
		/// Creates a new rule base
		/// </summary>
		private RuleBase()
		{
			this.affectedMembers = new List<string>();
		}

		/// <summary>
		/// Adds the name of an affected member (attribute or property)
		/// </summary>
		/// <param name="member">The name of the affected member</param>
		/// <returns>This rule base instance</returns>
		protected RuleBase AddAffectedMember(string member)
		{
			if (String.IsNullOrWhiteSpace(member))
				throw new ArgumentNullException("member");
			if (!this.affectedMembers.Contains(member))
				this.affectedMembers.Add(member);
			return this;
		}

		/// <summary>
		/// Gets all members (properties or attributes) of the domain object affected by this domain rule
		/// </summary>
		public virtual string[] AffectedMembers
		{
			get
			{
				return this.affectedMembers.ToArray();
			}
		}

		/// <summary>
		/// Clears the list of affected members
		/// </summary>
		protected void ClearAffectedMembers()
		{
			this.affectedMembers.Clear();
		}

		/// <summary>
		/// Checks all rules in the given input rule set and adds all broken rules found in
		/// the set to an existing result list
		/// </summary>
		/// <returns>The number of broken rules added to the result list</returns>
		/// <param name="input">The set of rules to check</param>
		/// <param name="result">The list of broken rules</param>
		public static int CollectBrokenRules(IEnumerable<IRule> input, IList<IRule> result)
		{
			if (input == null)
				throw new ArgumentNullException("input");
			if (result == null)
				throw new ArgumentNullException("output");

			IEnumerable<IRule> brokenRules = input.Where(x => x.IsValid == false).ToArray();
			lock (result)
			{
				foreach (IRule rule in brokenRules)
				{
					result.Add(rule);
				}	
			}
			return brokenRules.Count();
		}

		/// <summary>
		/// Gets the rule identifier
		/// </summary>
		public virtual string Id
		{
			get
			{
				return this.id;
			}
		}

		/// <summary>
		/// Gets a value indicating whether the domain object complies with the rule
		/// </summary>
		public abstract bool IsValid
		{
			get;
		}

		/// <summary>
		/// Removes the affected member with the given name
		/// </summary>
		/// <returns>True if the member was removed, false otherwise</returns>
		/// <param name="member">The name of the affected member</param>
		protected bool RemoveAffectedMember(string member)
		{
			if (String.IsNullOrWhiteSpace(member))
				throw new ArgumentNullException("member");
			return this.affectedMembers.Remove(member);
		}

	}

}